#include "exchange_amx.h"

#include "ExchangeTopoShape.h"
#include "common/system_log.h"
#include "nlohmann/json.hpp"
#include "utils/json_util.h"
#include <cstddef>
#include <fstream>
#include <memory>
#include <sstream>
#include <topology/TopoShape.hpp>
#include <topology/TopologyMacros.hpp>
#include <vector>
#include "occtio/OCCTTool.hpp"
#include "common/IndexSet.hpp"
#include "topology/TopoExplorerTool.hpp"
#include "utils/file_util.h"
#include "utils/string_util.h"
#include "ExchangeDocumentInstance.h"
#include "ExchangeDocumentReference.h"
#include "ExchangeTopoShape.h"
#include "topology/TopoShape.hpp"
#include "topology/TopoCompound.hpp"
#include "ExchangeMaterials.h"
#include "topology/TopoCast.hpp"

#ifdef AMXSTORAGE_ENABLE
#include "ProjectInfo.h"
#endif

AMCAX_EXCHANGE_NAMESPACE_BEGIN

void ExchangeAmx::ReadFromFilePath()
{
#ifdef AMXSTORAGE_ENABLE
    LOG_DEBUG("read from file, path:{}", filePath_);
    auto amx_storage = AMCAX::STORAGE::AmxStorage::newInstance(AMCAX::STORAGE::StorageEnv::LOCAL);
    amx_storage->initLog(AMCAX::STORAGE::StorageLogLevel::Error, "");
    AMCAX::STORAGE::StorageConfig storageConfig;
    amx_storage->initDB(storageConfig);

    // NOTE:import into database using file path
    AMCAX::STORAGE::ProjectInfo project_info;
    LOG_DEBUG("ready to import amx to database");
    if (!amx_storage->importAmx(filePath_, project_info))
    {
        LOG_ERROR("import amx to database failed");
        return;
    }

    read_common(amx_storage);
    amx_storage->close();
#endif
}


void ExchangeAmx::ReadFromFileStream()
{
#ifdef AMXSTORAGE_ENABLE
    LOG_DEBUG("read from stream");
    auto amx_storage = AMCAX::STORAGE::AmxStorage::newInstance(AMCAX::STORAGE::StorageEnv::LOCAL);
    amx_storage->initLog(AMCAX::STORAGE::StorageLogLevel::Error, "");
    AMCAX::STORAGE::StorageConfig storageConfig;
    amx_storage->initDB(storageConfig);

    // NOTE:import into database using stream
    AMCAX::STORAGE::ProjectInfo project_info;
    LOG_DEBUG("ready to import amx to database");
    auto _amxStream = std::dynamic_pointer_cast<std::iostream>(inFileStreamAmx_);
    if (!amx_storage->importAmx(_amxStream, project_info))
    {
        LOG_ERROR("import amx to database failed");
        return;
    }
    read_common(amx_storage);
    amx_storage->close();
#endif
}

#ifdef AMXSTORAGE_ENABLE
void ExchangeAmx::read_common(std::shared_ptr<AMCAX::STORAGE::AmxStorage> amx_storage)
{
    int document_size = amx_storage->getDocumentSize();
    // int page_size = 10 ;
    // // int index = 0 ;
    // for (int index = 0  ; index < document_size ;) {
    // std::vector<AMCAX::STORAGE::DocumentInfo> document_list = amx_storage->getDocumentList(index,page_size) ;
    //     index += page_size ;
    //     /**
    //         limit(0,10)
    //         limit(10,10)
    //      */
    // }

    std::vector<AMCAX::STORAGE::DocumentInfo> document_list = amx_storage->getDocumentList(0, document_size);
    for (auto document_item : document_list)
    {
        LOG_DEBUG(
            "document name:{}  version:{} cax version:{}",
            document_item.getName(), document_item.getVersion(), document_item.getCaxVersion(), document_item.getDescription());
        std::string object_info = amx_storage->readObjects(document_item.getDocumentId(), document_item.getVersion());
        parse_object_list(amx_storage, document_item.getDocumentId(), document_item.getVersion(), object_info);
        amx_storage->deleteDocument(document_item.getDocumentId(), document_item.getVersion());
    }
}

void ExchangeAmx::parse_object_list(
    const std::shared_ptr<AMCAX::STORAGE::AmxStorage> amx_storage,
    const std::string document_id,
    const std::string document_version,
    std::string str_object_info)
{
    if (str_object_info.empty())
    {
        LOG_ERROR("object information is empty");
        return;
    }

    if (!nlohmann::json::accept(str_object_info))
    {
        LOG_ERROR("Invalid JSON format. {}", str_object_info);
        return;
    }

    std::string material_info = amx_storage->readMaterials(document_id, document_version);
    nlohmann::json json_material_info = {};
    if (!material_info.empty() && nlohmann::json::accept(material_info))
    {
        json_material_info = nlohmann::json::parse(material_info);
    }

    nlohmann::json json_object_info = nlohmann::json::parse(str_object_info);
    if (json_object_info.is_array())
    {
        for (auto object_item : json_object_info)
        {
            auto exchange_object = parse_object_item_recursion(amx_storage, document_id, document_version, object_item, json_material_info, "");
            if (nullptr != exchange_object)
            {
                exchangeObjectList_.push_back(exchange_object);
            }
        }
    }
}

std::shared_ptr<ExchangeObject> ExchangeAmx::parse_object_item_recursion(
    const std::shared_ptr<AMCAX::STORAGE::AmxStorage> amx_storage,
    const std::string document_id,
    const std::string document_version,
    const nlohmann::json &json_item,
    const nlohmann::json &json_materials_in_database,
    std::string prefix)
{
    if (!json_item.is_object())
    {
        LOG_ERROR("Invalid object item:{}", json_item.dump());
        return nullptr;
    }
    std::string object_id = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_ID);
    if (object_id.empty())
    {
        LOG_ERROR("Invalid object item:{}", json_item.dump());
        return nullptr;
    }
    std::shared_ptr<ExchangeObject> root_exchange_object(new ExchangeObject(object_id));
    nlohmann::json children = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_CHILDREN, nlohmann::json::object());
    if (!children.empty() && children.is_array())
    {
        for (auto child_item : children)
        {
            std::shared_ptr<ExchangeObject> exchange_children_item = parse_object_item_recursion(amx_storage, document_id, document_version, child_item, json_materials_in_database, prefix + " --- ");
            if (nullptr != exchange_children_item)
            {
                root_exchange_object->AddChild(exchange_children_item);
            }
        }
    }

    int sortId = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_SORT_ID, -1);

    std::string object_name = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_NAME);
    bool object_visibility = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_VISIBILITY, true);
    std::string type = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_TYPE);
    std::string shape = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_SHAPE);
    nlohmann::json params = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_PARAMS, nlohmann::json::object());
    // TODO: 需转成ExchangeObjectMaterial
    nlohmann::json materials = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_MATERIALS, nlohmann::json::object());
    nlohmann::json frame = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_FRAME, nlohmann::json::object());
    // bool negative  = json_util::get_json_value_by_key(json_item,OBJECT_ITEM_FIELD_NEGATIVE,false) ;
    // nlohmann::json track  = json_util::get_json_value_by_key(json_item,OBJECT_ITEM_FIELD_TRACK,nlohmann::json::object()) ;
    // nlohmann::json mark = json_util::get_json_value_by_key(json_item,OBJECT_ITEM_FIELD_MARK,nlohmann::json::object()) ;

    // LOG_DEBUG("object id:{} name:{} type:{} shape:{} params:{} materials:{} frame:{} negative:{} track:{} mark:{} children:{}",
    //     object_id,object_name,type,shape,params.dump(), materials.dump(),frame.dump(),negative,track.dump(),mark.dump(),children.dump()) ;
    std::string baseId = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_BASEID);
    nlohmann::json pre_links = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_PRE_LINKS, nlohmann::json::object());
    nlohmann::json out_links = json_util::get_json_value_by_key(json_item, OBJECT_ITEM_FIELD_OUT_LINKS, nlohmann::json::object());

    root_exchange_object->sortId = sortId;
    root_exchange_object->id = object_id;
    root_exchange_object->name = object_name;
    root_exchange_object->visibility = object_visibility;
    root_exchange_object->type = type;
    // root_exchange_object->parameters = params;
    root_exchange_object->baseId = baseId;
    LOG_INFO("{} -> sort id:{} name:{}, type:{}", prefix, sortId, object_name, type);
    if (pre_links.is_array())
    {
        for (auto pre_link_item : pre_links)
        {
            if (pre_link_item.is_string() && !pre_link_item.is_null())
            {
                root_exchange_object->prelinks.push_back(pre_link_item.get<std::string>());
            }
        }
    }
    if (out_links.is_array())
    {
        for (auto out_link_item : out_links)
        {
            if (out_link_item.is_string() && !out_link_item.is_null())
            {
                root_exchange_object->prelinks.push_back(out_link_item.get<std::string>());
            }
        }
    }

    // NOTE: get shape from db
    if (!shape.empty())
    {
        std::stringstream shape_stream;
        if (amx_storage->readShape(document_id, shape, document_version, shape_stream))
        {
            AMCAX::TopoShape s ;
            AMCAX::OCCTIO::OCCTTool::Read(s, shape_stream);
            root_exchange_object->shape = std::make_shared<ExchangeTopoShape>(s) ;
        }
    }
    // NOTE: setting material information
    if (
        !materials.empty() &&
        materials.is_object() &&
        materials.contains(MATERIALS_ITEM_FIELD_FACES) &&
        !materials[MATERIALS_ITEM_FIELD_FACES].is_null() &&
        materials[MATERIALS_ITEM_FIELD_FACES].is_array() &&
        !root_exchange_object->shape->GetShape().IsNull())
    {
        nlohmann::json faces = materials[MATERIALS_ITEM_FIELD_FACES];
        for (auto face_item : faces)
        {
            int index = json_util::get_json_value_by_key(face_item, MATERIALS_FACE_ITEM_FIELD_FACEID, -100);
            int matrials_id_in_db = json_util::get_json_value_by_key(face_item, MATERIALS_FACE_ITEM_FIELD_MATID_IN_DB, -100);
            if (
                (index < -1) ||
                (matrials_id_in_db < -1))
            {
                continue;
            }
            if (json_materials_in_database.empty() || !json_materials_in_database.is_array())
            {
                continue;
            }
            for (auto json_material_item : json_materials_in_database)
            {
                if (!json_materials_in_database.contains("id") || json_materials_in_database["id"].is_null() || !json_materials_in_database["id"].is_number_integer())
                {
                    continue;
                }
                std::string color = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_COLOR);
                ExchangeMaterialItem material(color);
                material.name = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_NAME);
                material.ambient = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_AMBIENT);
                material.diffuse = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_DIFFUSE);
                material.emissive = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_EMISSIVE);
                material.specular = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_SPECULAR);
                material.shininess = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_SHININESS, 0.0);
                material.transparency = json_util::get_json_value_by_key(json_material_item, MATERIAL_ITEM_IN_DATABSE_FIELD_TRANSPARENCY, 0.0);

                AMCAX::IndexSet<AMCAX::TopoShape> faces_;
                AMCAX::TopoExplorerTool::MapShapes(root_exchange_object->shape->GetShape(), AMCAX::ShapeType::Face, faces_);
                if (index < 0 || index > faces_.size())
                {
                    LOG_ERROR("Invalid index");
                    break;
                }

                if(!root_exchange_object->materials){
                    root_exchange_object->materials = std::make_shared<ExchangeMaterials>() ;
                }

                // root_exchange_object->materials.emplace(faces_[index], material);
                root_exchange_object->materials->AddMaterial(faces_[index], material) ;
                // break;
            }
        }
    }

    return root_exchange_object;
}
#endif

void ExchangeAmx::Write(const std::vector<std::shared_ptr<ExchangeObject>> &exchange_shapes, 
    const std::shared_ptr<ExchangeDocumentInstance> &exchangeRootDocumentInstance)
{
#ifdef AMXSTORAGE_ENABLE
    auto amx_storage = AMCAX::STORAGE::AmxStorage::newInstance(AMCAX::STORAGE::StorageEnv::LOCAL);
    amx_storage->initLog(AMCAX::STORAGE::StorageLogLevel::Error, "");
    AMCAX::STORAGE::StorageConfig storageConfig;
    amx_storage->initDB(storageConfig);

    if(!exchangeRootDocumentInstance)
    {
        LOG_ERROR("exchangeRootDocumentInstance is null");
        return;
    }

    exchangeDocumentIdMap_.clear();
    docRelations_.clear();
    parents_.clear();

    writeDocumentInstanceRecursion(amx_storage, exchangeRootDocumentInstance, true);

    // Clean up the document and storage.
    for (auto &iter : exchangeDocumentIdMap_)
    {
        amx_storage->deleteDocument(iter.second.refDocumentId);
    }
    amx_storage->close();
#endif
}

#ifdef AMXSTORAGE_ENABLE

nlohmann::json ExchangeAmx::write_object_item_recursion(const std::shared_ptr<ExchangeObject> &exchange_shape,
                                                        const std::shared_ptr<AMCAX::STORAGE::AmxStorage> &amx_storage,
                                                        const std::string &document_id, const std::string &document_version,
                                                        nlohmann::json &json_materials_in_database)
{
    json object_infos;
    object_infos[OBJECT_ITEM_FIELD_SORT_ID] = exchange_shape->sortId;
    object_infos[OBJECT_ITEM_FIELD_ID] = (!exchange_shape->id.empty()) ? exchange_shape->id : AMCAX::STORAGE::createDocumentId();
    object_infos[OBJECT_ITEM_FIELD_NAME] = exchange_shape->name;

    object_infos[OBJECT_ITEM_FIELD_VISIBILITY] = exchange_shape->visibility;
    bool isShapeNull = !exchange_shape->shape || exchange_shape->shape->GetShape().IsNull();
    if (isShapeNull)
    {
        object_infos[OBJECT_ITEM_FIELD_TYPE] = (!exchange_shape->type.empty()) ? exchange_shape->type : "GeomBox";
    }
    else
    {
        object_infos[OBJECT_ITEM_FIELD_TYPE] = (!exchange_shape->type.empty()) ? exchange_shape->type : "Part::Feature";
    }

    std::string shapeId{};
    if (!isShapeNull)
    {
        // shapeId = (!exchange_shape->shape_id.empty()) ? exchange_shape->shape_id : AMCAX::STORAGE::createDocumentId();
        std::shared_ptr<std::iostream> oo = std::make_shared<std::stringstream>();
        oo->precision(15);
        AMCAX::OCCTIO::OCCTTool::Write(exchange_shape->shape->GetShape(), *oo, false);
        amx_storage->writeShape(document_id, exchange_shape->id, document_version, oo);
        object_infos[OBJECT_ITEM_FIELD_SHAPE] = exchange_shape->id;
        LOG_INFO("writeShape: documentId {}, shapeId {}", document_id, exchange_shape->id);
    }

    json material_json = json::object();
    if (
        exchange_shape->materials && 
        !exchange_shape->materials->IsEmpty() &&
        !isShapeNull)
    {
        LOG_DEBUG("test write_object_item_recursion_materials");
        json face_json = json::array();
        // NOTE: 提取shape上的面信息
        AMCAX::IndexSet<AMCAX::TopoShape> faces_;
        AMCAX::TopoExplorerTool::MapShapes(exchange_shape->shape->GetShape(), AMCAX::ShapeType::Face, faces_);
        for (auto &[sub, mat] : exchange_shape->materials->GetMaterials())
        {
            if (!faces_.contains(sub))
            {
                continue;
            }
            int index = faces_.index(sub);
            json face_json_item = json::object();

            face_json_item[MATERIALS_FACE_ITEM_FIELD_FACEID] = faces_.index(sub);
            face_json_item[MATERIALS_FACE_ITEM_FIELD_MATID_IN_DB] = faces_.index(sub);
            face_json.emplace_back(face_json_item);
            // 保存到materia表的材质信息
            json material_infos = json::object();
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_ID] = faces_.index(sub);
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_NAME] = mat.name;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_COLOR] = mat.color_;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_TEXTTURE] = mat.texture;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_AMBIENT] = mat.ambient;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_DIFFUSE] = mat.diffuse;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_EMISSIVE] = mat.emissive;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_SPECULAR] = mat.specular;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_SHININESS] = mat.shininess;
            material_infos[MATERIAL_ITEM_IN_DATABSE_FIELD_TRANSPARENCY] = mat.transparency;
            json_materials_in_database.push_back(material_infos);
        }
        material_json[MATERIALS_ITEM_FIELD_FACES] = face_json;
    }
    object_infos[OBJECT_ITEM_FIELD_MATERIALS] = material_json;

    json mark = json::array();
    object_infos[OBJECT_ITEM_FIELD_MARK] = mark;

    json pre_link = json::array();
    for (auto pre_link_item : exchange_shape->prelinks)
    {
        json item = json::parse(pre_link_item);
        pre_link.push_back(item);
    }
    object_infos[OBJECT_ITEM_FIELD_PRE_LINKS] = pre_link;

    json out_link = json::array();
    for (auto out_link_item : exchange_shape->outlinks)
    {
        json item = json::parse(out_link_item);
        pre_link.push_back(item);
    }
    object_infos[OBJECT_ITEM_FIELD_PRE_LINKS] = out_link;

    object_infos[OBJECT_ITEM_FIELD_BASEID] = exchange_shape->baseId;

    json child_json = json::array();
    for (auto childk_item : exchange_shape->childs)
    {
        json child = write_object_item_recursion(childk_item, amx_storage, document_id, document_version, json_materials_in_database);
        child_json.push_back(child);
    }
    object_infos[OBJECT_ITEM_FIELD_CHILDREN] = child_json;

    object_infos[OBJECT_ITEM_FIELD_PARAMS] = json::object();

    // json track = json::array();
    // object_infos[OBJECT_ITEM_FIELD_TRACK] = track;

    return object_infos;
}

static nlohmann::json getTransformJson(std::shared_ptr<ExchangeDocumentInstance> instance)
{
    auto trsf = instance->GetTransform();
    auto orientation = trsf.GetOrientation();
    auto origin = trsf.GetOrigin();
    nlohmann::json matrix = {
        orientation[0][0],
        orientation[0][1],
        orientation[0][2],
        origin[0],
        orientation[1][0],
        orientation[1][1],
        orientation[1][2],
        origin[1],
        orientation[2][0],
        orientation[2][1],
        orientation[2][2],
        origin[2],
    };
    return matrix;
}

void ExchangeAmx::childInstancesToJson(const std::vector<std::shared_ptr<ExchangeDocumentInstance>>& childInstances, nlohmann::json& arr)
{
    for (auto instance : childInstances)
    {
        auto reference = instance->GetDocumentReference();
        if (exchangeDocumentIdMap_.find(reference->GetId()) != exchangeDocumentIdMap_.end())
        {
            auto docRefInfo = exchangeDocumentIdMap_[reference->GetId()];

            nlohmann::json childs = nlohmann::json::array();

            childInstancesToJson(reference->GetDirectChildInstances(), childs);

            nlohmann::json item = {
                { "dxid", AMCAX::STORAGE::createDocumentId()  },
                { "label", instance->GetName() },
                { "visibility", true },
                { "type", "Assembly::Component" },
                { "shape", "" },
                { "children", childs }
            };
            
            auto params =  json::object();
            params["time"] = 0;
            params["idmap"] = json::object();

            item["assembly"] = {
                {"docid", docRefInfo.refDocumentId},
                {"docver", docRefInfo.refVersion},
                {"asmtype", reference->IsPart() ? "part" : "subassembly"},
                {"matrix", getTransformJson(instance)},
                {"params", params}
            };

            arr.push_back(item);
        }
    }
}


AMCAX::STORAGE::DocumentRefInfo ExchangeAmx::writeAmxByDocumentReference(std::shared_ptr<AMCAX::STORAGE::AmxStorage> &amx_storage,
    const std::shared_ptr<ExchangeDocumentReference> &exchangeDocumentReference, bool isRoot)
{
    AMCAX::STORAGE::DocumentRefInfo documentRefInfo;
    if (!exchangeDocumentReference)
    {
        LOG_ERROR("exchangeDocumentReference is null");
        return documentRefInfo;
    }

    auto exchangeDocRefId = exchangeDocumentReference->GetId();
    if (exchangeDocumentIdMap_.find(exchangeDocRefId) != exchangeDocumentIdMap_.end())
    {
        return exchangeDocumentIdMap_[exchangeDocRefId];
    }
    
    std::string version = "1";
    auto documentId = AMCAX::STORAGE::createDocumentId();
    documentRefInfo.refDocumentId = documentId;
    documentRefInfo.refVersion = version;
    documentRefInfo.infos = ""; // TODO:    

    // write document info
    AMCAX::STORAGE::DocumentInfo info;
    info.setDocumentId(documentId)
        .setVersion(version)
        .setName(exchangeDocumentReference->GetName() + "[from exchange]");

    bool isPart = exchangeDocumentReference->IsPart();
    info.setType(isPart ? "part" : "product");

    amx_storage->writeDocument(info);
    LOG_INFO("writeDocument: {}, info: {}", documentId, info.getType());

    json properties_list = json::array();
    json Materials_DB = json::array();

    if(!isPart)
    {
        parents_.push_back(std::make_pair(documentId, version));
        // Remove existing ref document?
        amx_storage->removeRefDocument(documentId, version);
        size_t childCount = exchangeDocumentReference->GetDirectChildInstances().size();
        LOG_INFO("{} has {} child instance", exchangeDocRefId, childCount);
        for (auto &childInstance : exchangeDocumentReference->GetDirectChildInstances())
        {
            std::shared_ptr<ExchangeObject> exchangeObject = std::make_shared<ExchangeObject>(string_util::generate_UUID());
            exchangeObject->type = "Assembly::Component";
            exchangeObject->name = childInstance->GetName();

            // Dump shape info to json.
            auto childReference = childInstance->GetDocumentReference();
            auto childDocumentRefInfo = writeAmxByDocumentReference(amx_storage, childReference, false);
            
            auto jsonObject = write_object_item_recursion(exchangeObject, amx_storage, documentId, version, Materials_DB);

            jsonObject["assembly"]["docid"] = childDocumentRefInfo.refDocumentId;
            jsonObject["assembly"]["docver"] = childDocumentRefInfo.refVersion;
            jsonObject["assembly"]["matrix"] = getTransformJson(childInstance);
            jsonObject["assembly"]["asmtype"] = childReference->IsPart() ? "part" : "subassembly";
            //jsonObject["assembly"]["label"] = childReference->GetName();
            auto params =  json::object();
            params["time"] = 0;
            params["idmap"] = json::object();
            jsonObject["assembly"]["params"] = params;

            json child_json = json::array();
            if (!childReference->IsPart())
            {
                childInstancesToJson(childReference->GetDirectChildInstances(), child_json);
            }
            jsonObject[OBJECT_ITEM_FIELD_CHILDREN] = child_json;
            // Write instance shape info to database.
            std::string object_id = json_util::get_json_value_by_key(jsonObject, OBJECT_ITEM_FIELD_ID);
            amx_storage->writeObject(documentId, object_id, version, jsonObject.dump());
            LOG_INFO("writeObject documentID {}, object:{}", documentId, jsonObject.dump());

            // amx_storage->addRefDocument(documentId, version, childDocumentRefInfo);

            for (int i = 0; i < parents_.size(); ++i)
            {
                auto relation = std::make_pair(parents_[i].first, childDocumentRefInfo.refDocumentId);
                if (docRelations_.find(relation) != docRelations_.end())
                {
                    continue;
                }
                docRelations_.insert(relation);
                
                amx_storage->addRefDocument(parents_[i].first, parents_[i].second, childDocumentRefInfo);
            }
            // Add direct child instance to the properties list.
            properties_list.emplace_back(object_id);
        }
        parents_.pop_back();
    }
    else
    {
        // Part中第一个ChildNode必须是GeomBox, 所有其他feature 都包含在这个GeomBox里面
                
        /*

        {
        "baseid": "",
        "children": [
            {
            "baseid": "",
            "children": [],
            "dxid": "c6622a45-9047-49b2-9bc4-9e0ea3edc4ea",
            "label": "拉伸.1",
            "mark": [],
            "outlinks": [],
            "params": {
            },
            "prelinks": [
            ],
            "shape": "c6622a45-9047-49b2-9bc4-9e0ea3edc4ea",
            "sortid": 4,
            "tags": {},
            "type": "Part::Extrude",
            "visibility": true
            }
        ],
        "dxid": "76a3fcea-a738-4759-8f3a-ad24d9e6a878",
        "label": "零件几何体.1",
        "mark": [],
        "outlinks": [],
        "prelinks": [],
        "shape": "",
        "sortid": 0,
        "type": "GeomBox",
        "visibility": true
        }

        */
        std::shared_ptr<ExchangeObject> exchangeObjectRoot = std::make_shared<ExchangeObject>(string_util::generate_UUID());
        exchangeObjectRoot->type = "GeomBox";
        //exchangeObjectRoot->name = exchangeDocumentReference->GetName();

        AMCAX::TopoCompound topoCompound;
        std::shared_ptr<ExchangeTopoShape> exchangeShape = nullptr ;
        exchangeDocumentReference->GetPartShape(exchangeShape);
        if (exchangeShape) {
            if (!exchangeShape->GetShape().IsNull() &&
                ShapeType::Compound == exchangeShape->GetShape().Type()
            ) 
            {
                topoCompound =  AMCAX::TopoCast::Compound(exchangeShape->GetShape()) ;
            }            
        }


        if (!topoCompound.IsNull())
        {
            std::shared_ptr<ExchangeObject> exchangeObject = std::make_shared<ExchangeObject>(string_util::generate_UUID());
            exchangeObject->type = "Part::Feature";
            exchangeObject->shape = std::make_shared<ExchangeTopoShape>(topoCompound);
            exchangeObjectRoot->AddChild(exchangeObject);
        }

        // Dump shape info to json.
        auto jsonObject = write_object_item_recursion(exchangeObjectRoot, amx_storage, documentId, version, Materials_DB);
        std::string object_id = json_util::get_json_value_by_key(jsonObject, OBJECT_ITEM_FIELD_ID);

        // Write instance shape info to database.
        amx_storage->writeObject(documentId, object_id, version, jsonObject.dump());
        LOG_INFO("writeObject documentID {}, object:{}", documentId, jsonObject.dump());


        properties_list.emplace_back(exchangeObjectRoot->id);
    }

    amx_storage->writeMaterials(documentId, version, Materials_DB.dump());

    json property = {
        {"CoreVersion", "0.4.2"},
        {"docVer", version},
        {"Name", "cad"},
        {"Unit", "mm"},
        {"CreatedBy", "exchange"},
        {"CreationDate", string_util::get_current_time()},
        {"LastModifiedDate", string_util::get_current_time()},
        {"List", properties_list},
        {"Tracks", json::array()}};
    amx_storage->writeProperty(documentId, version, "cad", property.dump());
    LOG_INFO("writeProperty documentId:{}, property:{}", documentId, property.dump());

    AMCAX::STORAGE::ProjectInfo project_info;
    std::unordered_map<std::string, std::string> documents;
    documents[documentId] = version;
    // project_info.setProperties(properties);
    project_info.setDocuments(documents);

    exchangeDocumentIdMap_[exchangeDocRefId] = documentRefInfo;

    // TODO: polish sub assemblies/parts names.
    if (isRoot)
    {
        auto tempFilePath = filePath_;
        if (OUTPUT_FILE_TYPE::FILE_PATH == outputFileType_)
        {
            if (AMCAX::Exchange::file_util::is_file_exist(tempFilePath))
            {
                LOG_ERROR("file<{}> already exist", tempFilePath);
                return documentRefInfo;
            }

            if (isPart)
            {
                LOG_DEBUG("write to file, path:{}", tempFilePath);
                if (!amx_storage->exportAmx(tempFilePath, project_info))
                {
                    LOG_ERROR("export amx file failed");
                    return documentRefInfo;
                }
            }
            else
            {
                LOG_DEBUG("write to file, path:{}", tempFilePath);
                if (!amx_storage->exportAmx(tempFilePath, project_info))
                {
                    LOG_ERROR("export amx file failed");
                    return documentRefInfo;
                }
            }

            return documentRefInfo;
        }
        else
        {
            // NOTE: stream output is not supported, may need to be implemented in the future.
            LOG_ERROR("output file type:{} is unknown", (int)outputFileType_);
            return documentRefInfo;
        }   
    }
 

    return documentRefInfo;
}

AMCAX::STORAGE::DocumentRefInfo ExchangeAmx::writeDocumentInstanceRecursion(std::shared_ptr<AMCAX::STORAGE::AmxStorage> &amx_storage,
                                                                            const std::shared_ptr<ExchangeDocumentInstance> &exchangeDocumentInstance, bool isRoot)
{
    AMCAX::STORAGE::DocumentRefInfo documentRefInfo;
    if (!exchangeDocumentInstance)
    {
        LOG_ERROR("exchangeDocumentInstance is null");
        return documentRefInfo;
    }
    auto exchangeDocumentReference = exchangeDocumentInstance->GetDocumentReference();
    if (!exchangeDocumentReference)
    {
        LOG_ERROR("exchangeDocumentReference is null");
        return documentRefInfo;
    }

    bool isPart = exchangeDocumentReference->IsPart();


    std::string version = "0.4.3";
    auto exchangeDocRefId = exchangeDocumentReference->GetId();
    LOG_INFO("writeDocumentInstanceRecursion exchangeDocRefId: {}, isPart: {}", exchangeDocRefId, isPart);
    std::string documentId;
    if (exchangeDocumentIdMap_.find(exchangeDocRefId) != exchangeDocumentIdMap_.end())
    {
        documentRefInfo = exchangeDocumentIdMap_[exchangeDocRefId];
    }
    else
    {
        documentRefInfo = writeAmxByDocumentReference(amx_storage, exchangeDocumentReference, isRoot);
    }
    
    // // Dump child transformation info to json.
    // auto trsf = exchangeDocumentInstance->GetTransform();
    // auto orientation = trsf.GetOrientation();
    // auto origin = trsf.GetOrigin();
    // json matrix = {
    //     orientation[0][0],
    //     orientation[0][1],
    //     orientation[0][2],
    //     origin[0],
    //     orientation[1][0],
    //     orientation[1][1],
    //     orientation[1][2],
    //     origin[1],
    //     orientation[2][0],
    //     orientation[2][1],
    //     orientation[2][2],
    //     origin[2],
    // };
    // jsonObject["assembly"]["matrix"] = matrix;
    // jsonObject["assembly"]["asmtype"] = isPart ? "part" : isRoot ? "assembly"
    //                                                              : "subassembly";
        // Dump child transformation info to json.
    return documentRefInfo;
}

#endif

AMCAX_EXCHANGE_NAMESPACE_END